package kr.ac.hanyang.cs.park;

import android.util.Log;

public class EndPointDetection {
	private short[] aInput;
	private short[] aOutput;

	private int iFrameCntPer200ms;
	private int iFrameCntPer1ms;
	private int iChunkSize;
	private int iTotalChunkCnt;
	private int iSkipChunkCnt;

	private final double THRESHOLD									= 0.30;

	
	public EndPointDetection(short[] aInput, int iSampleRate) {
		this.aInput													= aInput;
		this.iFrameCntPer1ms										= iSampleRate / 1000;			// 1 frame/ms
		this.iFrameCntPer200ms										= iFrameCntPer1ms * 200;		// sample for 200 ms
		this.iChunkSize												= 10 * iFrameCntPer1ms;			// we'll check per 10 frames 
		this.iTotalChunkCnt											= (int)Math.floor(aInput.length / iChunkSize);
		this.iSkipChunkCnt											= iFrameCntPer200ms / iChunkSize;

		Log.i("EPD", "Sampling rate: "+ iSampleRate);						// expect 8000 samples/sec
		Log.i("EPD", "No. of frames per 1ms: "+ iFrameCntPer1ms);			// expect 8 for 8000 samples/sec
		Log.i("EPD", "No. of frames per 200ms: "+ iFrameCntPer200ms);		// expect 1600 for 8000 samples/sec
	}

	
	// reference: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.138.623&rep=rep1&type=pdf
	public short[] detectEndPoint() {

		// Step 1. Calculate the mean value for first 200ms
		long lSum													= 0;
		double dMeanVal												= 0.0;

		for (int i = 0;i < iFrameCntPer200ms;i++) {
			lSum													+= Math.abs(aInput[i]);
		}
		dMeanVal													= (double) (lSum / iFrameCntPer200ms);

		Log.i("EPD", "lSum: "+ lSum);
		Log.i("EPD", "dMeanVal: "+ dMeanVal);


		// Step 2. Calculate standard deviation value for first 200ms
		double dSDVal												= 0.0;
		lSum														= 0;

		for (int i = 0;i < iFrameCntPer200ms;i++) {
			lSum													+= Math.pow((Math.abs(aInput[i]) - dMeanVal), 2);
		}
		dSDVal														= (double) Math.sqrt(lSum / iFrameCntPer200ms);

		Log.i("EPD", "lSum: "+ lSum);
		Log.i("EPD", "dSDVal: "+ dSDVal);


		// Step 3. Check each value is voice or not
		//			- using 'Mahalanobis Distance' value :: subtracting its mean, getting absolute value and dividing by its standard deviation
		boolean[] aIsVoice											= new boolean[aInput.length];

		for (int i = 0;i < aInput.length;i++) {
			if ((Math.abs(Math.abs(aInput[i]) - dMeanVal) / dSDVal) > THRESHOLD) {
				aIsVoice[i]											= true;
			}
			else {
				aIsVoice[i]											= false;
			}
		}


		// Step 4. Classify voice chunk and non-voice chunk
		//			- check majority of each chunk, and mark it to the aIsVoiceChunk array
		boolean[] aIsVoiceChunk										= new boolean[iTotalChunkCnt];
		int iVoiceChunkCnt											= 0;
 
		for (int i = iSkipChunkCnt;i < iTotalChunkCnt;i++) {
			int iChunkStart											= i * iChunkSize;
			int iChunkEnd											= iChunkStart + iChunkSize;

			int iVoiceCnt											= 0;
			int iNonVoiceCnt										= 0;

			
			// check
			for (int j = iChunkStart;j < iChunkEnd;j++) {
				if (aIsVoice[j])
					iVoiceCnt++;
				else
					iNonVoiceCnt++;
			}
			
			// mark
			Log.i("VoiceCnt",Integer.toString(i) + " : " + Integer.toString((iVoiceCnt-iNonVoiceCnt)));
//			if (iVoiceCnt > iNonVoiceCnt) {
			if (iVoiceCnt - iNonVoiceCnt > 100) {
				aIsVoiceChunk[i]									= true;
				iVoiceChunkCnt++;
			}
			else {
				aIsVoiceChunk[i]									= false;
			}
		}


		// Step 5. Generating output by gathering voice chunk (skip non-voice chunk)
		int iVoiceFrameCnt											= iVoiceChunkCnt * iChunkSize;
		int iFrameIdx												= 0;
		aOutput														= new short[iVoiceFrameCnt];

		for (int i = iSkipChunkCnt;i < iTotalChunkCnt;i++) {
			if (aIsVoiceChunk[i]) {
				int iChunkStart										= i * iChunkSize;
				int iChunkEnd										= iChunkStart + iChunkSize;

				for (int j = iChunkStart;j < iChunkEnd;j++) {
					aOutput[iFrameIdx]								= aInput[j];
					iFrameIdx++;
				}
			}
		}
		

		return aOutput;
	}
}